<script setup>
import { ref, watch, onMounted, onUnmounted } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

const container = ref()
const props = defineProps({
  width: [String, Number],
  height: [String, Number],
  params: {
    type: Object,
    default() {
      return {
        radius: 5,
        data: [
          {
            label: '',
            value: 0,
            color: '#fff'
          }
        ]
      }
    }
  }
})
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, props.width / props.height, 0.1, 1000)

const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })

const controls = new OrbitControls(camera, renderer.domElement)
controls.autoRotate = true

const light = new THREE.AmbientLight(0x505050)
scene.add(light)
const pointLight = new THREE.PointLight(0xffffff, 10, 32, 3)
pointLight.position.set(0, 15, 5)
pointLight.castShadow = true
scene.add(pointLight)

renderer.setSize(props.width, props.height)
// camera.position.set(0, 8, 18)
camera.position.set(5, 15, 12)

let allArc = new THREE.Group()

const radiusOut = 10 // 外环半径
let allNum = props.params.data.reduce((pre, cur) => (pre += cur.value), 0)
let startDeg = 0
let depthMax = 10 // 最高

// 绘制环形
const drawArc = (item) => {
  let ratio = item.value / allNum
  let endDeg = Math.PI * 2 * ratio + startDeg
  let depth = depthMax * ratio
  if (depth < 1) depth = 1

  const shape = new THREE.Shape()
  shape.absarc(0, 0, radiusOut, startDeg, endDeg)
  shape.absarc(0, 0, props.params.radius, endDeg, startDeg, true)

  const extrudeSettings = {
    steps: 2,
    depth: depth,
    bevelEnabled: false
  }
  let color = item.color ?? `#${Math.random().toString().slice(2, 8)}`
  const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)
  const material = new THREE.MeshStandardMaterial({
    color: color,
    transparent: true,
    opacity: 0.8,
    emissive: color,
    emissiveIntensity: 0.3
  })
  const mesh = new THREE.Mesh(geometry, material)
  mesh.castShadow = true
  allArc.add(mesh)

  startDeg = endDeg

  // 绘制label
  let canvas = document.createElement('canvas')
  canvas.setAttribute('width', '100')
  canvas.setAttribute('height', '100')
  let ctx = canvas.getContext('2d')
  ctx.font = '15px math'
  ctx.textAlign = 'center'
  ctx.strokeStyle = '#fff'
  ctx.fillStyle = 'rgba(65, 165, 251, 1)'
  ctx.textBaseline = 'bottom'
  ctx.fillText(item.label, 50, 89)
  ctx.font = '13px math'
  ctx.strokeStyle = '#f00'
  ctx.fillStyle = color
  ctx.textBaseline = 'bottom'
  ctx.fillText(item.value, 50, 100)
  ctx.measureText(item.label)
  const map = new THREE.TextureLoader().load(canvas.toDataURL())
  const materialText = new THREE.SpriteMaterial({ map: map })
  materialText.sizeAttenuation = false
  const sprite = new THREE.Sprite(materialText)
  sprite.scale.set(0.6, 0.6, 0.6)
  sprite.center.set(0.5, 0)
  const box = new THREE.Box3()
  let position = box.setFromObject(mesh).getCenter(new THREE.Vector3())
  let position1 = position.clone()
  position1.z = depth + 3
  // 绘制线
  const materialLine = new THREE.LineBasicMaterial({
    color: color,
    linewidth: 2
  })
  let hengXianChang = 5 // 横线长
  let hengXian = position1.clone().projectOnPlane(new THREE.Vector3(0, 0, 1)).normalize()
  let position2 = position1.clone().addScaledVector(hengXian, hengXianChang)
  const points = []
  points.push(position)
  points.push(position1)
  points.push(position2)
  const geometryLine = new THREE.BufferGeometry().setFromPoints(points)
  const line = new THREE.Line(geometryLine, materialLine)
  allArc.add(line)

  sprite.position.copy(position2)
  allArc.add(sprite)
}

const setOption = () => {
  scene.remove(allArc)
  allArc = new THREE.Group()
  allNum = props.params.data.reduce((pre, cur) => (pre += cur.value), 0)
  startDeg = 0

  // 绘制底盘
  const shape = new THREE.Shape()
  shape.absarc(0, 0, radiusOut + 1, 0, Math.PI)
  shape.absarc(0, 0, radiusOut + 1, Math.PI, 0)
  const extrudeSettings = {
    steps: 2,
    depth: 0.5,
    bevelEnabled: false
  }
  const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)
  const material = new THREE.MeshPhongMaterial({
    color: `#155a81`
  })
  const mesh = new THREE.Mesh(geometry, material)
  mesh.castShadow = true
  mesh.position.z = -1
  allArc.add(mesh)
  // 旋转环
  allArc.rotateX(-Math.PI / 2)
  scene.add(allArc)

  props.params.data.forEach((item) => drawArc(item))
}

let aniId = null
const animate = () => {
  aniId = requestAnimationFrame(animate)
  controls.update()
  renderer.render(scene, camera)
}

watch(
  () => props.params.data,
  () => {
    setOption()
  },
  { deep: true }
)
onMounted(() => {
  container.value.appendChild(renderer.domElement)
  setOption()
  animate()
})

onUnmounted(() => {
  cancelAnimationFrame(aniId)
})
</script>

<template>
  <div ref="container" :style="{ width: `${width}px`, height: `${height}px` }"></div>
</template>

<style scoped></style>
